#
#      Copyright 2011 Miranda, Tom
#       
#       Licensed under the Apache License, Version 2.0 (the "License");
#       you may not use this file except in compliance with the License.
#       You may obtain a copy of the License at
#
#          http://www.apache.org/licenses/LICENSE-2.0
#
#       Unless required by applicable law or agreed to in writing, software
#       distributed under the License is distributed on an "AS IS" BASIS,
#       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#       See the License for the specific language governing permissions and
#       limitations under the License.
#

import mc, threading, time, calendar, sage, airings
from threading import RLock
from time import sleep
from time import strftime
from datetime import date

# Puts the time into a format consumable by the timebar.
def timeToListItems(t):
	items = mc.ListItems()
	for i in range(5):
		item = mc.ListItem(mc.ListItem.MEDIA_UNKNOWN)
		timeSec = t + (i*30*60)
		timeTuple = time.localtime(timeSec)
		minute = timeTuple.tm_min
		if minute > 30:
			minuteStr = '30'
		else:
			minuteStr = '00'
		item.SetLabel(str(timeTuple.tm_hour)+':'+minuteStr)
		items.append(item)	
	return items

# Puts the time into a format consumable by the datebar.
def timeToDatebar(t):
	items = mc.ListItems()
	item = mc.ListItem(mc.ListItem.MEDIA_VIDEO_EPISODE)
	timeTuple = time.localtime(t)
	item.SetLabel(strftime("%a, %b %d",timeTuple))
	items.append(item)
	return items
	
def timeToDatebarOLD(t):
	items = mc.ListItems()
	item = mc.ListItem(mc.ListItem.MEDIA_VIDEO_EPISODE)
	item.SetLabel(strftime("%a, %b %d",t))
	items.append(item)
	return items

def channelListToListItems(list, t, api):
	'''Convert a list of Sage Channel JSON objects to a Boxee ListItems object.

		  list: A list of dictionary objects; each dictionary represents a Sage Channel
		return: A Boxee ListItems object; the contents is each dictionary converted to a Boxee ListItem
	'''
	items = mc.ListItems()
	for i in list:
		item = _chanToListItem(i, t, api)
		if(item != None):
			items.append(item)
	return items

def _chanToListItem(chan, t, api):
	try:
		item = mc.ListItem(mc.ListItem.MEDIA_UNKNOWN)
		item.SetAddToHistory(False)
		
		# Label will hold the channel name. (LIFE)
		if(chan.has_key('ChannelName') and chan['ChannelName'] != ''):		
			item.SetLabel(chan['ChannelName'])
		else:		
			item.SetLabel('Unknown')
			
		# Custom property ChannelDescription will hold the description. (LifeTime Channel).  Can't get SetDescription to work?
		if(chan.has_key('ChannelDescription') and chan['ChannelDescription'] != ''):
			item.SetProperty('ChannelDescription', str(chan['ChannelDescription']))		
		else:
			item.SetProperty('ChannelDescription', 'Unknown')
				
		# Episode will hold the channel number.
		if(chan.has_key('ChannelNumber') and chan['ChannelNumber'] != ''):
			#item.SetEpisode(int(chan['ChannelNumber']))		
			item.SetProperty('ChannelNumber', str(chan['ChannelNumber']))
		else:
			#item.SetEpisode(0)
			item.SetProperty('ChanelNumber', '0')
			
		# Custom property StationID will hold the StationID. Needed if we need to use the Sage Channel Object.
		if(chan.has_key('StationID') and chan['StationID'] != ''):
			item.SetProperty('StationID', str(chan['StationID']))
		else:
			item.SetProperty('StationID', '0')
		
		logoUrl = api.getChannelLogoUrl(item.GetLabel())
		#logoUrl = 'http://HARRIS:8080/temp.png'
		print "logoUrl="+logoUrl
		item.SetIcon(str(logoUrl))
		item.SetProperty('LogoUrl', str(logoUrl))
		#item.SetImage(0, logoUrl)
		item.SetThumbnail(str(logoUrl))
		
		return item
		
	except IndexError:
		LOG.LogDebug('sage.py: Exception in _chanToListItem')
		return None
	
# Get the entire 5 x 5 set of airings for the EPG grid.
def getEpgAiringList(index, channels, t, api):

	# Get the recording MediaFiles.
	recordingMediaFiles = api.GetCurrentlyRecordingMediaFiles()
	
	newList = mc.ListItems()
	newIndex = index
	for c in range(5):
		chan = channels[newIndex]
		newIndex = getNextChannelIndex(newIndex, channels)		
		stationID = chan.GetProperty('StationID')
		rowList = getRow(stationID, t, recordingMediaFiles, api)
		
		for i in range(5):
			newList.append(rowList[i])
			
	return newList

def getEpgAiringListOLD(index, channels, t, api):

	# Get the recording MediaFiles.
	recordingMediaFiles = api.GetCurrentlyRecordingMediaFiles()
	
	newList = mc.ListItems()
	rollIndex = 0
	for c in range(5):
	
		if index+c < len(channels):
			chan = channels[index+c]
		else:
			chan = channels[rollIndex]
			rollIndex += 1
			
		stationID = chan.GetProperty('StationID')
		
		rowList = getRow(stationID, t, recordingMediaFiles, api)
		
		for i in range(5):
			newList.append(rowList[i])
			
	return newList

def getNextChannelIndex(currentIndex, channels):
	if (currentIndex + 1) < len(channels):
		return currentIndex + 1
	else:
		return 0
		
def getPrevChannelIndex(currentIndex, channels):
	if (currentIndex-1) < 0:
		return (len(channels)-1)
	else:
		return (currentIndex-1)
	
def getRowAtIndexAtTime(index, channels, t, api):

	if index >= len(channels):
		mc.LogDebug("SageBoxee: Index out of bounds.")
		mc.LogDebug("SageBoxee: Index = " + str(index))
		mc.LogDebug("SageBoxee: Channels = " + str(channels))
		return None
		
	# Get the recording MediaFiles.
	recordingMediaFiles = api.GetCurrentlyRecordingMediaFiles()

	chan = channels[index]
	stationID = chan.GetProperty('StationID')
	return getRow(stationID, t, recordingMediaFiles, api)

# Returns a ListItems with the newRow as the first 5 ListItems.
def replaceFirstRow(oldList, newRow):
	newList = mc.ListItems()
	for i in range(5):
		newList.append(newRow[i])
	for i in range(20):
		newList.append(oldList[i])
	return newList

# Returns a ListItems with the newRow as the last 5 ListItems.
def replaceLastRow(oldList, newRow):
	newList = mc.ListItems()
	for i in range(20):
		newList.append(oldList[5+i])
	for i in range(5):
		newList.append(newRow[i])
	return newList

# Returns a ListItems with the specified newRow replacing the row at specified index. 
def replaceRow(oldList, newRow, index):
	newList = mc.ListItems()
	if index == 0:
		for i in range(5):
			newList.append(newRow[i])
		for i in range(20):
			newList.append(oldList[5+i])
	elif index == 1:
		for i in range(5):
			newList.append(oldList[i])
		for i in range(5):
			newList.append(newRow[i])
		for i in range(15):
			newList.append(oldList[10+i])			
	elif index == 2:
		for i in range(10):
			newList.append(oldList[i])
		for i in range(5):
			newList.append(newRow[i])
		for i in range(10):
			newList.append(oldList[15+i])		
	elif index == 3:
		for i in range(15):
			newList.append(oldList[i])
		for i in range(5):
			newList.append(newRow[i])
		for i in range(5):
			newList.append(oldList[20+i])		
	elif index == 4:
		for i in range(20):
			newList.append(oldList[i])
		for i in range(5):
			newList.append(newRow[i])		
	else:
		mc.LogError("SageBoxee: Invalid index passed to replaceRow.")
		return oldList
	return newList	
	
# Return the 5 ListItems on the station at 30 minute intervals.
def getRow(stationID, t, recordingMediaFiles, api):
	newList = mc.ListItems()
	
	i = 0
	nextSlotIsSame = False
	while i < 5:

		if not nextSlotIsSame:
			airing = sage.getAiringOnChannelAtTime(stationID, t+(i*30*60*1000), api)
			item = airings.airingToListItem(airing, api)

			item = airings.setIsRecording(item, recordingMediaFiles)
			item = airings.setThumbUrl(item, api)
		
			if airings.willBeRecorded(item, api):
				item.SetProperty('WillBeRecorded', 'True')
				
			expr = 'IsFavorite(GetAiringForID(%s))' % item.GetProperty('AiringID')
			if api.EvaluateExpression(expr):
				item.SetProperty('IsFavorite', 'True')
		
			expr = 'IsManualRecord(GetAiringForID(%s))' % item.GetProperty('AiringID')
			if api.EvaluateExpression(expr):
				item.SetProperty('IsManualRecord', 'True')
				
			newList.append(item)
			
		else:
			# Manually clone the object.  Can't get copy.copy() to work properly for some reason.
			newItem = airings.airingToListItem(airing, None)
			newItem.SetProperty('IsRecording', item.GetProperty('IsRecording'))
			newItem.SetProperty('ThumbImage', item.GetProperty('ThumbImage'))
			newItem.SetIcon(item.GetIcon())
			newItem.SetThumbnail(item.GetThumbnail())
			newItem.SetImage(0, item.GetImage(0))
			newItem.SetProperty('WillBeRecorded', item.GetProperty('WillBeRecorded'))
			newItem.SetProperty('IsFavorite', item.GetProperty('IsFavorite'))
			newItem.SetProperty('IsManualRecord', item.GetProperty('IsManualRecord'))
			newList.append(newItem)
		
		if i == 0 or not isSameAiringItem(newList[i-1], newList[i]): 
			newList[i].SetProperty('ShowTitle', 'true')
		else:
			newList[i].SetProperty('ShowTitle', '')
			
		i += 1
		
		# No need to fetch the airing again if its end time extends into the slot time.
		airingEndTime = long(item.GetProperty('ScheduleEndTimeMillis'))
		slotTime = long(t+(i*30*60*1000))
		nextSlotIsSame = airingEndTime >= slotTime
	
	return newList

def replaceFirstColumn(origList, newColumn):
	#for i in range(5):
	#	origList.insert(int(5*i), newColumn[i])
	#	origList.pop(int((5*i)+1))
	newList = mc.ListItems()
	for row in range(5):
		for col in range(5):
			if col==0:
				newList.append(newColumn[row])
			else:
				newList.append(origList[(row*5)+col-1])
				
	return setShowTitleForGrid(newList)
	
def replaceLastColumn(origList, newColumn):
	#for i in range(5):
	#	origList.insert(int(5+(5*i)), newColumn[i])
	#	origList.pop(int(5+(5*i)+1))
	newList = mc.ListItems()
	for row in range(5):
		for col in range(5):
			if col==4:
				newList.append(newColumn[row])
			else:
				newList.append(origList[(row*5)+col+1])
	return setShowTitleForGrid(newList)

def getColumn(index, channels, t, recordingMediaFiles, api):
	columnList = mc.ListItems()
	allAirings = sage.getAiringsOnViewableChannelsAtTime(t, api)
	if allAirings == '':
		mc.LogDebug('SageBoxee: Failed to find airings for time %s' % str(t))
		return ''
	rollIndex = 0
	for i in range(5):
		if index+i < len(channels):
			chanItem = channels[index+i]
		else:
			chanItem = channels[rollIndex]
			rollIndex += 1
		channelName = chanItem.GetLabel()
		airing = getAiringForChannel(channelName, allAirings)
		if airing == '':
			mc.LogDebug('SageBoxee: Failed to find airing for channel %s' % channelName)
		else:
			mc.LogDebug('SageBoxee: Found airing %s for channel %s' % (airing['AiringTitle'], channelName))
			
			# Get the metadata that exists in the JSON Object.
			item = airings.airingToListItem(airing, api)
			
			# Set the rest of the metadata that depends on additional sagex calls.
			if airings.willBeRecorded(item, api):
				item.SetProperty('WillBeRecorded', 'True')
				
			item = airings.setIsRecording(item, recordingMediaFiles)
			
			item = airings.setThumbUrl(item, api)
			
			expr = 'IsFavorite(GetAiringForID(%s))' % item.GetProperty('AiringID')
			if api.EvaluateExpression(expr):
				item.SetProperty('IsFavorite', 'True')
		
			expr = 'IsManualRecord(GetAiringForID(%s))' % item.GetProperty('AiringID')
			if api.EvaluateExpression(expr):
				item.SetProperty('IsManualRecord', 'True')

			columnList.append(item)
	return columnList
	
def setHasAnyIcons(item):
	if item.GetProperty('IsHDTV') != '' or item.GetProperty('IsWatched') != '' or item.GetProperty('IsShowFirstRun') != '' or item.GetProperty('IsLibraryFile') != '' or item.GetProperty('IsDontLike') != '' or item.GetProperty('IsRecording') != '' or item.GetProperty('IsFavorite') != '' or item.GetProperty('IsManualRecord') != '' or item.GetProperty('WillBeRecorded') != '':
		item.SetProperty('HasIcon', 'True')
	return item
	
def setShowTitleForGrid(gridItems):
	for row in range(5):
		thisRowIndex = row*5
			
		gridItems[thisRowIndex+0].SetProperty('ShowTitle', 'true')
	
		if not isSameAiringItem(gridItems[thisRowIndex+0], gridItems[thisRowIndex+1]):
			gridItems[thisRowIndex+1].SetProperty('ShowTitle', 'true')
		
		if not isSameAiringItem(gridItems[thisRowIndex+1], gridItems[thisRowIndex+2]):
			gridItems[thisRowIndex+2].SetProperty('ShowTitle', 'true')
		
		if not isSameAiringItem(gridItems[thisRowIndex+2], gridItems[thisRowIndex+3]):
			gridItems[thisRowIndex+3].SetProperty('ShowTitle', 'true')
			
		if not isSameAiringItem(gridItems[thisRowIndex+3], gridItems[thisRowIndex+4]):
			gridItems[thisRowIndex+4].SetProperty('ShowTitle', 'true')
	return gridItems
	
def getAiringForChannel(channelName, airings):
	for airing in airings:
		if (airing.has_key('Channel') and airing['Channel'] != ''):
			channel = airing['Channel']
			if (channel.has_key('ChannelName') and channel['ChannelName'] != ''):
				if (channel['ChannelName']==channelName):
					return airing
	return ''
	
def isSameAiringItem(item1, item2):
	return item1.GetProperty('AiringID') == item2.GetProperty('AiringID')
	
def	setMetadataOLD(item, airing):

	# Set the title.
	item.SetLabel(str(airing['AiringTitle']))
	item.SetTVShowTitle(str(airing['AiringTitle']))
	
	# Will need AiringID to take action on the Airing.
	item.SetProperty('AiringID', str(airing['AiringID']))
	
	item.SetGenre('None')
	
	# Set the more detailed genre, if it's available.
	#if (airing.has_key('Show') and airing['Show'].has_key('ShowCategoriesString') and airing['Show']['ShowCategoriesString'] != ''):
	#	item.SetGenre(str(airing['Show']['ShowCategoriesString']))
		
	item = sage.setAiringGenreAndBackground(item, airing)
		
	if (airing.has_key('Show') and airing['Show'].has_key('ShowDescription')):		
		item.SetDescription(airing['Show']['ShowDescription'])
		
	if(airing.has_key('AiringStartTime')):
		startTime = float(airing['AiringStartTime'] // 1000)
		airDate = date.fromtimestamp(startTime)
		airTime = strftime('%H:%M', time.localtime(startTime))
		item.SetProperty('AiringStartTime', str(airDate.month) + '/' + str(airDate.day) + '/' + str(airDate.year) + ' ' + airTime)
	else:
		item.SetProperty('AiringStartTime', 'Unknown')
		
	return item
	
# Checks if an airing is currently recording (accepts an AiringID as a string as input)
def isAiringRecording(a, api):
	expr = 'IsFileCurrentlyRecording(GetMediaFileForAiring(GetAiringForID(%s)))' % a
	return api.EvaluateExpression(expr)

def startRecording(airing, sageAPI):
	mc.LogDebug('SageBoxee: epg.py: Before sageAPI.Record')
	try:
		sageAPI.Record(airing)
	except:
		airing.SetProperty("WillBeRecorded", "True")
		mc.LogDebug('SageBoxee: epg.py: After sageAPI.Record')

def cancelRecording(airing, sageAPI):
	mc.LogDebug('SageBoxee: epg.py: Before sageAPI.CancelRecord')
	sageAPI.CancelRecord(airing)
	airing.SetProperty("WillBeRecorded", "False")
	mc.LogDebug('SageBoxee: epg.py: After sageAPI.CancelRecord')

def getMediaFileForAiring(airing, sageAPI):
	return sageAPI.GetMediaFileForAiring(airing)


# Outline, in case we need to cache airing data to improve performance.	
class GuideDataThread(threading.Thread):
	_status = dict()
	_keepAlive = True
	_api = None
	_lock = None

	def __init__(self, api):
		threading.Thread.__init__(self)
		self._api = api
		self._lock = RLock()

	def run(self):
		mc.LogDebug('SageBoxee: epg.py: Guide data thread started.')
		
		now = time.time()
		slotSizeMinutes = 30
		slotSizeSeconds = slotSizeMinutes*60
		slotsPerDay = (24*60)/slotSizeMinutes
		lookAheadDays = 3
		lookAheadSlots = slotsPerDay*lookAheadDays
		mc.LogDebug('SageBoxee: epg.py: Slots per day = %s' % str(slotsPerDay))
		mc.LogDebug('SageBoxee: epg.py: Lookahead slots = %s' % str(lookAheadSlots))

		expr = 'Sort(FilterByBoolMethod(GetAllChannels(), "IsChannelViewable", true), false, "ChannelNumber")'
		channels = self._api.EvaluateExpression(expr)
		mc.LogDebug('SageBoxee: epg.py: Found %s channels.' % str(len(channels)))
		
		for channel in channels:
			mc.LogDebug('SageBoxee: epg.py: Channel=%s' % str(channel['ChannelName']))
			for slot in range(lookAheadSlots):
				airing = sage.getAiringOnChannelAtTime(channel['StationID'], now*1000 + (slot*slotSizeSeconds*1000), self._api)
				mc.LogDebug('SageBoxee: epg.py: Airing=%s' % str(airing['AiringTitle']))

		while 1:
			try:
				self._lock.acquire()
				

	
				if(not self._keepAlive):
					break
			finally:
				self._lock.release()
			sleep(10)
		mc.LogDebug('SageBoxee: epg.py: Guide data thread has terminated!')

	def getStatus(self, id = None):
		self._lock.acquire()
		if(id == None):
			rc = self._status.copy()
		else:
			rc = self._status[id]
		self._lock.release()
		return rc

	def setKeepAlive(self, val):
		self._lock.acquire()
		self._keepAlive = val
		self._lock.release()
		
def stopGuideDataThread(t):
	t.setKeepAlive(False)
